package org.fao.oekc.agris.inputRecords.parser;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

import jfcutils.files.write.TXTWriter;

import org.fao.oekc.agris.inputRecords.dom.AGRISAttributes;
import org.fao.oekc.agris.inputRecords.dom.AgrisApDoc;
import org.fao.oekc.agris.inputRecords.util.CheckIndex;
import org.fao.oekc.agris.util.EscapeXML;
import org.fao.oekc.agris.util.IssnCleaner;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * Parses AGRICOLA MODS files
 * @author celli
 *
 */
public class AgricolaSaxParser extends DefaultHandler {

	//parser
	private List<AgrisApDoc> records;
	private AgrisApDoc current;

	//index checker
	private CheckIndex indexChecker;

	//excluded records
	List<String> excludedTitles;

	//data
	private boolean isTitle;
	private boolean isAlternative;
	private boolean isTitleContent;
	private boolean isSubTitleContent;

	private boolean isRelatedItem;
	private boolean isGenre;
	private boolean isDate;
	private boolean isJournalTitle;

	private boolean isISSN;
	private boolean isISBN;
	private boolean isURI;

	private boolean isCreatorPersonal;
	private boolean isCreatorCorporate;
	private boolean isCreatorConference;
	private boolean isCreatorName;
	private boolean isPublisherPlace;

	private boolean isPartOf;
	private boolean isSubject;
	private boolean isTopic;
	private boolean isAgrovoc;
	private boolean isLanguage;
	private boolean isExtent;
	private boolean isFormat;
	private boolean isNote;

	//to read the entire content
	private StringBuffer buffer; 

	/**
	 * Create the parser for the document and start parsing.
	 * All booleans set to false until the corresponding element starts
	 * @param records list of result records
	 * @param globalDuplicatesRemoval if true, remove records whose ISSN is in the index, if false remove records if the ISSN is in the index associated to the same country code
	 * @param issnToBeIncluded lines of the input text file (also null, it is optional), used for example to filter ISSNs
	 */
	public AgricolaSaxParser(List<AgrisApDoc> records) {
		try {
			this.indexChecker = new CheckIndex();
			this.excludedTitles = new LinkedList<String>();
			this.records = records;

			this.isTitle = false;
			this.isAlternative = false;
			this.isTitleContent = false;
			this.isSubTitleContent = false;
			this.isRelatedItem = false;
			this.isGenre = false;
			this.isDate = false;
			this.isNote = false;

			this.isPartOf = false;
			this.isURI = false;
			this.isCreatorPersonal = false;
			this.isCreatorName = false;
			this.isPublisherPlace = false;
			this.isSubject = false;
			this.isTopic = false;
			this.isAgrovoc = false;
			this.isLanguage = false;
			this.isExtent = false;
			this.isFormat = false;
			this.isCreatorCorporate = false;
			this.isCreatorConference = false;
			this.isJournalTitle = false;
			this.isISSN = false;
			this.isISBN = false;
		}
		catch(Exception e) {
			e.printStackTrace();
		}
	}

	////////////////////////////////////////////////////////////////////
	// Event handlers.
	////////////////////////////////////////////////////////////////////
	/**
	 * Recognize an element
	 */
	public void startElement (String namespaceURI, String localName, String rawName, Attributes atts) {		

		//RECORD
		if(rawName.equalsIgnoreCase("mods")){
			this.current = new AgrisApDoc();
		}
		else
			if(this.current!=null) {
				//exclude series
				if(rawName.equalsIgnoreCase("genre")){
					String authority = atts.getValue("authority");
					if(authority!=null && authority.equalsIgnoreCase("marc")){
						this.buffer = new StringBuffer();
						this.isGenre = true;
					}	
				}
				else
					//title
					if(rawName.equalsIgnoreCase("titleInfo") && !this.isRelatedItem){
						String type = atts.getValue("type");
						if(type!=null && type.equalsIgnoreCase("alternative"))
							this.isAlternative = true;
						else
							this.isTitle = true;
					}
					else
						//main title
						if(rawName.equalsIgnoreCase("title") && (this.isTitle || this.isAlternative || this.isJournalTitle)){
							this.buffer = new StringBuffer();
							this.isTitleContent = true;
						}
						else
							//supplement title
							if(rawName.equalsIgnoreCase("subTitle") && this.isTitle){
								this.buffer = new StringBuffer();
								this.isSubTitleContent = true;
							}
							else
								//related item
								if(rawName.equalsIgnoreCase("relatedItem")){
									this.isRelatedItem = true;
									String type = atts.getValue("type");
									if(type!=null && type.equalsIgnoreCase("preceding"))
										this.isJournalTitle = true;
								}
								else
									//date
									if(rawName.equalsIgnoreCase("dateIssued")){
										String encoding = atts.getValue("encoding");
										String point = atts.getValue("point");
										if(encoding!=null && encoding.equalsIgnoreCase("marc")){
											if(point==null || !point.equalsIgnoreCase("end")){
												this.buffer = new StringBuffer();
												this.isDate = true;
											}
										}	
										else {
											//normal dateIssued
											this.buffer = new StringBuffer();
											this.isDate = true;
										}
									}
									else
										//identifiers
										if(rawName.equalsIgnoreCase("identifier")){
											String type = atts.getValue("type");
											if(type!=null && !type.equalsIgnoreCase("lccn")){
												if(type.equalsIgnoreCase("isbn"))
													this.isISBN = true;
												else if(type.equalsIgnoreCase("issn"))
													this.isISSN = true;
												else if(type.equalsIgnoreCase("uri"))
													this.isURI = true;
												this.buffer = new StringBuffer();
											}
										}
										else
											//creators
											if(rawName.equalsIgnoreCase("name")){
												String type = atts.getValue("type");
												if(type!=null){
													if(type.equalsIgnoreCase("personal"))
														this.isCreatorPersonal = true;
													else if(type.equalsIgnoreCase("corporate"))
														this.isCreatorCorporate = true;
													else if(type.equalsIgnoreCase("conference"))
														this.isCreatorConference = true;
												}
											}
											else
												//creator name
												if(rawName.equalsIgnoreCase("namePart") && (this.isCreatorPersonal || this.isCreatorCorporate || this.isCreatorConference)){
													this.buffer = new StringBuffer();
													this.isCreatorName = true;
												}
												else
													//publisher place
													if(rawName.equalsIgnoreCase("placeTerm")){
														String type = atts.getValue("type");
														if(type!=null && type.equalsIgnoreCase("text")){
															this.buffer = new StringBuffer();
															this.isPublisherPlace = true;
														}
													}
													else
														//medium
														if(rawName.equalsIgnoreCase("typeOfResource")){
															this.buffer = new StringBuffer();
															this.isFormat = true;
														}
														else
															//extent
															if(rawName.equalsIgnoreCase("extent")){
																this.buffer = new StringBuffer();
																this.isExtent = true;
															}
															else
																//subject
																if(rawName.equalsIgnoreCase("subject")){
																	String authority = atts.getValue("authority");
																	if(authority!=null && authority.equalsIgnoreCase("lcsh"))
																		this.isSubject = true;
																}
																else
																	//topic
																	if(rawName.equalsIgnoreCase("topic") && this.isSubject){
																		this.buffer = new StringBuffer();
																		this.isTopic = true;
																	}
																	else
																		//agrovoc
																		if(rawName.equalsIgnoreCase("geographic") && this.isSubject){
																			this.buffer = new StringBuffer();
																			this.isAgrovoc = true;
																		}
																		else
																			//language
																			if(rawName.equalsIgnoreCase("languageTerm")){
																				this.buffer = new StringBuffer();
																				this.isLanguage = true;
																			}
																			else
																				//isPartOf
																				if(rawName.equalsIgnoreCase("url")){
																					this.buffer = new StringBuffer();
																					this.isPartOf = true;
																				}
																				else
																					//note
																					if(rawName.equalsIgnoreCase("note")){
																						this.buffer = new StringBuffer();
																						this.isNote = true;
																					}
			}
	}

	/**
	 * Extract content from XML
	 * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
	 */
	public void characters(char[] ch, int start, int length) {

		if(this.current!=null) {
			//BUFFER reader
			if(this.isTitleContent || this.isSubTitleContent || this.isGenre || this.isDate || this.isISBN
					|| this.isISSN || this.isURI || this.isCreatorName || this.isPublisherPlace
					|| this.isFormat || this.isExtent || this.isTopic || this.isAgrovoc || this.isLanguage
					|| this.isPartOf || this.isNote){
				this.buffer.append(ch, start, length);
			}
		}
	}

	/**
	 * The end of an element. For big elements like ABSTRACT, to allow the buffering of all content
	 * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
	 */
	public void endElement(String namespaceUri, String localName, String rawName)
	throws SAXException {

		if(this.current!=null) {

			//RECORD
			if(rawName.equalsIgnoreCase("mods")){
				//mandatory fields
				if(this.current.getTitle2language().size()>0 && this.current.getDateIssued()!=null)
					this.records.add(current);
				else
					this.excludedTitles.add("no date: "+this.current.getTitle2language());
			}
			else
				//discard series
				if(rawName.equalsIgnoreCase("genre") && this.isGenre){
					this.isGenre = false;
					String term = new String(this.buffer);
					if(term!=null && !term.trim().equals("")){
						if(term.equalsIgnoreCase("series")){
							this.excludedTitles.add("SERIES: "+this.current.getTitle2language());
							this.current = null;
						}
						else
							this.current.addType(term);
					}
				}
				else
					// TITLE
					if(rawName.equalsIgnoreCase("title") && this.isTitle && this.isTitleContent){
						this.isTitleContent  = false;
						String term = new String(this.buffer);
						if(term!=null && !term.trim().equals("")){
							//search Solr to see if title exists
							int occurrences = this.indexChecker.checkTitle(term);
							if(occurrences==0) {
								term = EscapeXML.getInstance().removeHTMLTagsAndUnescape(term);
								this.current.addTitle(term, "");
							}
							else {
								this.excludedTitles.add(term);
								this.current = null;
							}
						}
					}
					else
						//alternative
						if(rawName.equalsIgnoreCase("title") && this.isAlternative && this.isTitleContent){
							this.isTitleContent  = false;
							String term = new String(this.buffer);
							if(term!=null && !term.trim().equals("")){
								term = EscapeXML.getInstance().removeHTMLTagsAndUnescape(term);
								this.current.addAlternative(term, "");
							}
						}
						else
							//journal title
							if(rawName.equalsIgnoreCase("title") && this.isTitleContent && this.isJournalTitle){
								this.isTitleContent  = false;
								String term = new String(this.buffer);
								if(term!=null && !term.trim().equals("")){
									term = EscapeXML.getInstance().removeHTMLTagsAndUnescape(term);
									this.current.setCitationTitle(term);
								}
							}
							else
								//supplement
								if(rawName.equalsIgnoreCase("subTitle") && this.isSubTitleContent && this.isTitle){
									this.isSubTitleContent  = false;
									String term = new String(this.buffer);
									if(term!=null && !term.trim().equals("")){
										term = EscapeXML.getInstance().removeHTMLTagsAndUnescape(term);
										this.current.addSupplement(term);
									}
								}
								else
									//main_title
									if(rawName.equalsIgnoreCase("titleInfo")){
										this.isAlternative = false;
										this.isTitle = false;
										this.isJournalTitle = false;
									}
									else
										//related item
										if(rawName.equalsIgnoreCase("relatedItem")){
											this.isRelatedItem = false;
										}
										else
											//date
											if(rawName.equalsIgnoreCase("dateIssued") && this.isDate){
												this.isDate  = false;
												String term = new String(this.buffer);
												if(term!=null && !term.trim().equals("")){
													this.current.setDateIssued(term);
												}
											}
											else
												//identifiers
												if(rawName.equalsIgnoreCase("identifier")){
													//isbn
													if(this.isISBN){
														this.isISBN = false;
														String term = new String(this.buffer);
														if(term!=null && !term.trim().equals("")){
															this.current.addIdentifier(term, "ags:ISBN");
														}
													}
													else if(this.isISSN){
														this.isISSN = false;
														String term = new String(this.buffer);
														if(term!=null && !term.trim().equals("")){
															//naive issn cleaning and add -
															if(!IssnCleaner.isISSN(term))
																term = IssnCleaner.cleanISSN(term);
															if(IssnCleaner.isISSN(term))
																this.current.addIssn(term);
														}
													}
													else if(this.isURI){
														this.isURI = false;
														String term = new String(this.buffer);
														if(term!=null && !term.trim().equals("")){
															this.current.addIdentifier(term, "dcterms:URI");
														}
													}
												}
												else
													//creators
													if(rawName.equalsIgnoreCase("name")){
														this.isCreatorPersonal = false;
														this.isCreatorCorporate = false;
														this.isCreatorConference = false;
													}
													else 
														//creator name
														if(rawName.equalsIgnoreCase("namePart") && this.isCreatorName){
															this.isCreatorName  = false;
															String term = new String(this.buffer);
															if(term!=null && !term.trim().equals("")){
																if(this.isCreatorPersonal)
																	this.current.addCreatorPersonal(term);
																else if(this.isCreatorCorporate)
																	this.current.addCreatorCorporate(term);
																else if(this.isCreatorConference)
																	this.current.addCreatorConference(term);
															}
														}
														else
															//publisher place
															if(rawName.equalsIgnoreCase("placeTerm") && this.isPublisherPlace){
																this.isPublisherPlace = false;
																String term = new String(this.buffer);
																if(term!=null && !term.trim().equals("")){
																	this.current.addPublisherPlace(term);
																}
															}
															else
																//typeOfResource
																if(rawName.equalsIgnoreCase("typeOfResource") && this.isFormat){
																	this.isFormat = false;
																	String term = new String(this.buffer);
																	if(term!=null && !term.trim().equals("")){
																		this.current.setFormat(term);
																	}
																}
																else
																	//extent
																	if(rawName.equalsIgnoreCase("extent") && this.isExtent){
																		this.isExtent = false;
																		String term = new String(this.buffer);
																		if(term!=null && !term.trim().equals("")){
																			this.current.setExtent(term);
																		}
																	}
																	else
																		//subject
																		if(rawName.equalsIgnoreCase("subject")){
																			this.isSubject = false;
																		}
																		else
																			//topic
																			if(rawName.equalsIgnoreCase("topic") && this.isTopic){
																				this.isTopic = false;
																				String term = new String(this.buffer);
																				if(term!=null && !term.trim().equals("")){
																					AGRISAttributes attr = new AGRISAttributes();
																					attr.setLang("eng");
																					attr.setScheme("dcterms:LCSH");
																					this.current.addAgrovoc(term, attr);
																				}
																			}
																			else
																				//agrovoc
																				if(rawName.equalsIgnoreCase("geographic") && this.isAgrovoc){
																					this.isAgrovoc = false;
																					String term = new String(this.buffer);
																					if(term!=null && !term.trim().equals("")){
																						this.current.addSpatial(term);
																					}
																				}
																				else
																					//language
																					if(rawName.equalsIgnoreCase("languageTerm")){
																						this.isLanguage = false;
																						String term = new String(this.buffer);
																						if(term!=null && !term.trim().equals("")){
																							this.current.addLanguage(term, "dcterms:ISO639-2");
																						}
																					}
																					else
																						//isPartOf
																						if(rawName.equalsIgnoreCase("url")){
																							this.isPartOf = false;
																							String term = new String(this.buffer);
																							if(term!=null && !term.trim().equals("")){
																								this.current.addIsPartOfRelation(term, "dcterms:URI");
																							}
																						}
																						else
																							//note
																							if(rawName.equalsIgnoreCase("note")){
																								this.isNote = false;
																								String term = new String(this.buffer);
																								if(term!=null && !term.trim().equals("")){
																									String oldNote = this.current.getDescrNotes();
																									if(oldNote!=null)
																										term = oldNote+" "+term;
																									this.current.setDescrNotes(term);
																								}
																							}
																							else
																								//end document
																								if(rawName.equalsIgnoreCase("modsCollection")){
																									TXTWriter writer = new TXTWriter();
																									try {
																										writer.writeTXTLines(this.excludedTitles, "C:/Users/celli/Documents/workspace_agris/agris_input/agricola/excluded.txt");
																									} catch (IOException e) {
																										e.printStackTrace();
																									}
																									//System.out.println(this.excludedTitles);
																								}
		}
	}

}
